<?php

namespace Trawind\Basics\Traits;

use GuzzleHttp\Client;
use GuzzleHttp\Promise;
use Illuminate\Support\Str;
use Trawind\Helpers\HttpClientHelper;
use Trawind\Helpers\SystemHelper;
use Trawind\Helpers\UserHelper;
use Psr\Http\Message\ResponseInterface;

trait GuzzleHttpClient
{

    /**
     * @var string $domain
     */
    public $domain;


    /**
     * @var string $url
     */
    private $url;


    /**
     * @var array
     */
    protected $clientOptions = ['query' => ['source' => 'remote']];


    /**
     * @var null|Client
     */
    private static $_client = null;


    /**
     * @param  string  $domain
     * @return $this
     */
    public function setDomain(string $domain)
    {
        $this->domain = $domain;
        return $this;
    }


    /**
     * @param  string  $url
     * @return $this
     */
    public function setUrl(string $url)
    {
        $this->url = $url;
        return $this;
    }


    /**
     * @param  array  $options
     * @return $this
     */
    public function setClientOptions(array $options, bool $isRemote = true)
    {
        $this->clientOptions = $options;
        if ($isRemote) {
            $query = $this->clientOptions['query'] ?? [];
            $query['source'] = 'remote';
            $this->clientOptions['query'] = $query;
        }
        return $this;
    }


    /**
     * Get the result returned by the server.Private internal request without SSL
     *
     * @param  string  $method
     * @param  bool    $isAsync
     * @return mixed|null
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @example  GET:  $this->setUrl('/users')->result('GET');
     * @example  POST: $this->setUrl('/users')->setClientOptions(['form_params'=>['name'=>'zhangsan']])->result('POST');
     * @example  delete、put、patch、options、head  ....
     */
    public function result(string $method = 'GET', bool $isAsync = true)
    {
        HttpClientHelper::requestLog([
            'to_url' => $this->domain.$this->url,
            'option' => $this->clientOptions,
            'current_url' => '('.request()->method().')'.urldecode(url()->full())
        ]);

        if ($isAsync) {
            return $this->resultAsync($method);
        }
        $result = $this->client()->request($method, $this->url, $this->clientOptions);
        return static::getResponseData($result);
    }


    /**
     * Get the result returned by the server.Private internal request without SSL
     *
     * @param  string  $method
     * @return mixed|null
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @example  GET:  $this->setUrl('/users')->resultAsync('GET');
     * @example  POST: $this->setUrl('/users')->setClientOptions(['form_params'=>['name'=>'zhangsan']])->resultAsync('POST');
     * @example  delete、put、patch、options、head  ....
     */
    public function resultAsync(string $method = 'GET')
    {
        $promise = $this->sendAsync($method)
            ->then(function (ResponseInterface $res) {
                return $res->getBody()->getContents();
            })
            ->wait();
        $data = json_decode($promise, true);
        return $data['data'] ?? null;
    }


    public function sendAsync(string $method = 'GET')
    {
        return $this->client()->requestAsync($method, $this->url, $this->clientOptions);
    }


    /**
     * Get GuzzleHttp instance
     * wiki: https://guzzle-cn.readthedocs.io/zh_CN/latest/quickstart.html#id2
     *
     * @return Client
     * @example $this->client()->post($url);
     * @example $this->client()->put($url);
     * @example $this->client()->patch($url);
     * @example $this->client()->delete($url);
     * @example $this->client()->get($url);
     */
    public function client($origin = null): Client
    {
        $requestId = request()->header('x-request-id', (string) Str::uuid());
        request()->headers->add(['x-request-id' => $requestId]);

        //        if (!self::$_client instanceof Client) {
        self::$_client = new Client([
            'base_uri' => $this->domain,
            'verify' => false,
            'headers' => [
                'Accept' => 'application/json',
                'x-request-id' => $requestId,
                'Authorization' => UserHelper::token(),
                'MS-Request-ServiceName' => config('app.name') ?? null,
                'MS-Request-UserInfo' => json_encode(($origin == 'user') ? null : UserHelper::userInfo()),
            ]
        ]);
        //        }
        return self::$_client;
        //        return new Client([
        //            'base_uri' => $this->domain,
        //            'verify' => false,
        //            'headers' => [
        //                'Accept' => 'application/json',
        //                'Authorization' => UserHelper::token(),
        //                'MS-Request-ServiceName' => config('app.name') ?? null,
        //                'MS-Request-UserInfo' => json_encode(($origin == 'user') ? null : UserHelper::userInfo()),
        //            ]
        //        ]);
    }


    /**
     * Parse the response data
     *
     * @param  ResponseInterface  $response
     * @return mixed|null
     */
    public static function getResponseData(ResponseInterface $response)
    {
        $data = json_decode($response->getBody()->getContents(), true);
        return $data['data'] ?? null;
    }

    public static function resolve($promises, $fields = 'id')
    {
        $results = Promise\unwrap($promises);
        $result = [];
        foreach ($results as $key => $response) {
            $data = static::getResponseData($response)['data'] ?? [];
            $result[$key] = array_column($data, null, is_array($fields) ? $fields[$key] : $fields);
        }
        return $result;
    }

}
